#include <jni.h>
#include <opencv2/opencv.hpp>  //引入openCV
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/features2d.hpp>
#include <vector>
#include <android/log.h>

#define LOG_TAG "JPEG_JNI"
#define DEBUG
#define ANDROID_PLATFORM

#ifdef DEBUG
#ifdef ANDROID_PLATFORM
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))


#else
#define LOGD(fmt, ...) printf(fmt"\n", ##__VA_ARGS__)
		#define LOGI(fmt, ...) printf(fmt"\n", ##__VA_ARGS__)
		#define LOGW(fmt, ...) printf(fmt"\n", ##__VA_ARGS__)
		#define LOGE(fmt, ...) printf(fmt"\n", ##__VA_ARGS__)
#endif
#else
#define LOGD(...);
	#define LOGI(...);
	#define LOGW(...);
	#define LOGE(...);
#endif


using namespace std;
using namespace cv;

extern "C" {
JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial2_Tutorial2Activity_FindFeatures(JNIEnv*, jobject, jlong boxGray, jlong addrGray, jlong addrRgba);

JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial2_Tutorial2Activity_FindFeatures(JNIEnv*, jobject, jlong boxGray, jlong addrGray, jlong addrRgba)
{
    Mat& mGr  = *(Mat*)addrGray;

	// 创建SIFT特征提取
	auto detector = SIFT::create();
	vector<KeyPoint> keypoints_sence;
	Mat descriptors_sence;
	detector->detectAndCompute(mGr, Mat(), keypoints_sence, descriptors_sence);
    LOGD("kony: scene keypoints: %d\n", keypoints_sence.size());

    if(keypoints_sence.size() > 0) {
        Mat& mBoxGr  = *(Mat*)boxGray;
        Mat& mRgb = *(Mat*)addrRgba;

        vector<KeyPoint> keypoints_obj;
        Mat descriptors_box;
        detector->detectAndCompute(mBoxGr, Mat(), keypoints_obj, descriptors_box);
        LOGD("kony: box keypoints: %d\n", keypoints_obj.size());

        // 初始化flann匹配
        vector<DMatch> matches;
        Ptr<FlannBasedMatcher> matcher = FlannBasedMatcher::create();
        matcher->match(descriptors_box, descriptors_sence, matches);

        // 发现高质量匹配
        LOGD("kony: total matches: %d\n", matches.size());
        int numOfGood = matches.size() * 0.15;
        std::sort(matches.begin(), matches.end());
        matches.erase(matches.begin() + numOfGood, matches.end());
        LOGD("kony: good matches: %d\n", matches.size());

        // 抽取匹配描述子对应的关键点
        std::vector<Point2f> obj_pts;
        std::vector<Point2f> scene_pts;
        for (size_t i = 0; i < matches.size(); i++)
        {
            obj_pts.push_back(keypoints_obj[matches[i].queryIdx].pt);
            scene_pts.push_back(keypoints_sence[matches[i].trainIdx].pt);
        }

        // 对象对齐与单应性矩阵求解
        Mat H = findHomography(obj_pts, scene_pts, RANSAC);
        std::vector<Point2f> obj_corners(4);
        obj_corners[0] = Point(0, 0);
        obj_corners[1] = Point(mBoxGr.cols, 0);
        obj_corners[2] = Point(mBoxGr.cols, mBoxGr.rows);
        obj_corners[3] = Point(0, mBoxGr.rows);
        std::vector<Point2f> scene_corners(4);

        Mat src = InputArray(obj_corners).getMat(), m = InputArray(H).getMat();
        int depth = src.depth(), scn = src.channels(), dcn = m.rows - 1;
        if (scn + 1 == m.cols) {
            perspectiveTransform(obj_corners, scene_corners, H);

            // 绘制发现的对象
            std::vector<Point> pts;
            for (int i = 0; i < scene_corners.size(); i++) {
                pts.push_back(scene_corners[i]);

                stringstream ss_point;
                ss_point << scene_corners[i].x << ":" << scene_corners[i].y;
                putText(mRgb, ss_point.str(), cv::Point(5, 30 + 40*i), cv::FONT_HERSHEY_PLAIN, 2, cv::Scalar(0, 255, 0), 2);
            }

            stringstream cols_rows;
            cols_rows << mRgb.cols << ":" << mRgb.rows;
            putText(mRgb, cols_rows.str(), cv::Point(5, 30 + 200), cv::FONT_HERSHEY_PLAIN, 2, cv::Scalar(0, 255, 0), 2);

            if (scene_corners[0].x > 0 && scene_corners[1].x && scene_corners[2].x > 0 && scene_corners[3].x > 0) {
                int x01 = abs(scene_corners[0].x - scene_corners[1].x);
                int x23 = abs(scene_corners[2].x - scene_corners[3].x);

                int x02 = abs(scene_corners[0].x - scene_corners[2].x);
                int x13 = abs(scene_corners[1].x - scene_corners[3].x);

                if (x01 < 100 && x23 < 100 && x02 > 50 && x13 > 50) {
                    polylines(mRgb, pts, true, Scalar(0, 255, 0), 10, 8, 0);
                }
            }
        }
    }
}
}
